import logging
import os
import re
import threading
import time
from datetime import datetime
from http import server
import logging
from selenium.webdriver.common.keys import Keys

import allure
import pytest
import uiautomator2 as u2
from time import sleep
from common.decs import relations
import time
from datetime import datetime

# 配置日志记录
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class App:
    def __init__(self):
        self.relations = {}
        self.driver = u2.connect_usb()
        self.package = ''
        self.driver.implicitly_wait(10)
        # 切换成fastInput输入法
        self.driver.set_fastinput_ime(True)

        # 引用style_number，生成款号add款号为 202400  每次生成都+1
        self.style_prefix = "add款号为 202400"
        try:
            with open('counter.txt', 'r') as f:
                self.style_counter = int(f.read())
        except FileNotFoundError:
            self.style_counter = 0

    # def style_number(self):
    #     self.style_counter += 1
    #     with open('counter.txt', 'w') as f:
    #         f.write(str(self.style_counter))
    #     return f"{self.style_prefix}{self.style_counter}"
    #
    # # 以上为款号生成

    # 自动生成款号
    def get_kh(self):
        # 获取当前时间
        now = datetime.now()

        # 格式化时间为带有前导零的字符串
        formatted_time = now.strftime("%d%H%M%S")

        # 生成款号并存储到 relations 字典中
        kh = "MHM" + formatted_time
        self.relations["kh"] = kh

        # 记录日志信息
        logger.info("款号：" + self.relations["kh"])

    def get_phone(self):
        # 获取当前时间
        now = datetime.now()

        # 格式化时间为带有前导零的字符串
        formatted_time = now.strftime("%d%H%M%S")

        # 生成款号并存储到 relations 字典中
        phone = "135" + formatted_time
        self.relations["phone"] = phone

        # 记录日志信息
        logger.info("手机号：" + self.relations["phone"])

    def dev_connect(self):
        """
        链接设备
        :param name:设备名称
        :return:
        """
        self.driver = u2.connect_usb()
        return self.driver

    def appstart(self, package=''):
        """
        打开app
        :param package:app的包名
        :return:
        """
        # 打开app
        try:
            self.driver.app_start(package)
            self.driver.implicitly_wait(10)
            self.package = package
            self.sleep(1)
        except Exception as e:
            self.close(package)
            self.driver.app_start(package)
            self.driver.implicitly_wait(10)
        return self.package

    def __find_ele(self, locator=""):
        """
        自动判断元素定位方法
        :param locator: 元素定位
        :return:
        """
        self.driver.implicitly_wait(10)
        # 如果元素以com开头，就用id定位
        if locator.startswith("com"):
            return self.driver(resourceId=locator)
        # 如果元素以//开头，就用xpath定位
        elif locator.startswith("//"):
            return self.driver.xpath(locator)
        # 否则用text定位
        else:
            return self.driver(textContains=locator)

    def click(self, locator1='', locator2=''):
        """
        点击操作
        :param locator:
        :return:
        # """
        # try:
        # 如果第二个元素存在，就用坐标点击
        if locator2:
            logging.info("点击坐标：(" + locator1 + "," + locator2 + ")")
            self.driver.click(float(locator1), float(locator2))
        # 否则就用element点击
        else:
            logging.info("点击元素：" + locator1)
            ele = self.__find_ele(locator1)
            ele.click()
        time.sleep(1)  # 添加强制等待 1 秒

    def double_click(self, xy='', time=''):
        """
        双击
        :param x:X坐标
        :param y:Y坐标
        :param time:双击时间间隔
        :return:
        """
        xy = xy.split(",")
        x = xy[0]
        y = xy[1]
        x = float(x)
        y = float(y)
        time = float(time)
        self.driver.double_click(x, y, time)
        time.sleep(1)  # 添加强制等待 1 秒
        return

    def long_click(self, locator="", locator1=""):
        """
        长按
        :param x:x坐标
        :param y: y坐标
        :param time: 长按时间
        :return:
        """
        # 如果第二个元素存在就按照坐标来长按
        if locator1:
            self.driver.long_click(float(locator), float(locator1), 2)
        else:
            ele = self.__find_ele(locator)
            ele.long_click()

    def Swipe(self, params=""):
        """
        滑动
        :param params: 滑动方向 up down right left
        :return:
        """
        time.sleep(1)  # 添加强制等待 1 秒
        self.driver.swipe_ext(params)

    # 固定向上滑动0.1秒，需要传参起始坐标X和Y
    def swipeUp(self, start_x_str, start_y_str):
        try:
            start_x = int(start_x_str)
            start_y = int(start_y_str)

            # 计算向上滑动后的结束坐标
            end_x = start_x
            end_y = int(start_y * 0.8)

            # 执行滑动操作
            self.driver.swipe(start_x, start_y, end_x, end_y, duration=0.1)
            time.sleep(1)  # 添加强制等待 1 秒

        except ValueError as ve:
            print(f"传入的参数无法转换为整数: {ve}")
        except Exception as e:
            print(f"滑动操作出现错误: {e}")

    # 固定向下滑动0.1秒，需要传参起始坐标X和Y
    def swipeDown(self, start_x_str, start_y_str):
        try:
            start_x = int(start_x_str)
            start_y = int(start_y_str)

            # 计算向下滑动后的结束坐标
            end_x = start_x
            end_y = int(start_y * 1.2)  # 向下滑动，y 坐标增大

            # 执行滑动操作
            self.driver.swipe(start_x, start_y, end_x, end_y, duration=0.1)
            time.sleep(1)  # 添加强制等待 1 秒

        except ValueError as ve:
            print(f"传入的参数无法转换为整数: {ve}")
        except Exception as e:
            print(f"滑动操作出现错误: {e}")

    # 固定向下滑动0.1秒，需要传参起始坐标X和Y
    def swipeLeft(self, start_x_str, start_y_str):
        try:
            start_x = int(start_x_str)
            start_y = int(start_y_str)

            # 计算向左滑动后的结束坐标
            end_x = int(start_x * 0.8)
            end_y = start_y

            # 执行滑动操作
            self.driver.swipe(start_x, start_y, end_x, end_y, duration=0.1)
            time.sleep(1)

        except ValueError as ve:
            print(f"传入的参数无法转换为整数: {ve}")
        except Exception as e:
            print(f"滑动操作出现错误: {e}")

    # 固定向右滑动0.1秒，需要传参起始坐标X和Y
    def swipeRight(self, start_x_str, start_y_str):
        try:
            start_x = int(start_x_str)
            start_y = int(start_y_str)

            # 计算向右滑动后的结束坐标
            end_x = int(start_x * 1.2)
            end_y = start_y

            # 执行滑动操作
            self.driver.swipe(start_x, start_y, end_x, end_y, duration=0.1)
            time.sleep(1)

        except ValueError as ve:
            print(f"传入的参数无法转换为整数: {ve}")
        except Exception as e:
            print(f"滑动操作出现错误: {e}")

    def Drag(self, x1, x2, y1, y2):
        time.sleep(1)
        if y2:
            x1 = float(x1)
            x2 = float(x2)
            y1 = float(y1)
            self.driver.drag(x1, y1, x2, y2, 0.1)
        else:
            x1 = float(x1)
            x2 = float(x2)
            y1 = float(y1)
            self.driver.drag(x1, y1, x2, y1, 0.1)
        # self.driver.drag(x1,y1,x2,y1,0.1)
        time.sleep(1)  # 添加强制等待 1 秒

    # 直接传入要点击的两个坐标
    def Drag_1(self, str1, str2):
        parts1 = str1.split(',')
        parts2 = str2.split(',')
        x1 = float(parts1[0])
        y1 = float(parts1[1])
        x2 = float(parts2[0])
        y2 = float(parts2[1])
        print("x1:" + str(x1))
        print("y1:" + str(y1))
        print("x2:" + str(x2))
        print("y2:" + str(y2))
        self.driver.drag(x1, y1, x2, y2, 0.1)

    @relations
    def input(self, locator='', value=''):
        """
        输入
        :param locator:
        :param value:
        :return:
        """
        # try:
        # 获取焦点
        # ele=self.__find_ele(locator)
        # ele.click()
        # self.driver.xpath(locator).click()

        # value为style_number时，引用自增长款号
        if value == "style_number":
            value = self.style_number()
        else:
            # 如果 value 不为空且不是 "style_number"，不进行操作
            pass

        self.click(locator)
        logging.info("输入：" + value)
        # 清除原来输入框内的的文本
        self.driver.clear_text()
        # 重新输入文本
        self.driver.send_keys(str(value), clear=True)
        # ele=self.__find_ele(locator)
        # ele.send_keys(value)
        print("输入：" + value)
        # self.driver.set_fastinput_ime(False)
        time.sleep(1)  # 添加强制等待 1 秒

    def search(self):
        logging.info("确定操作")
        self.driver.press("enter")

    @relations
    def get_text(self, locator='', reg=""):
        """获取文本信息"""
        ele = self.__find_ele(locator)
        info = ele.info
        # 如果这个元素包含text文本信息
        if info["text"]:
            text = info["text"]
            if reg:
                text = re.findall(r'\d{14}', text)[0]
                self.relations[reg] = text
            else:
                # 把文本信息存起来
                self.relations["text"] = text
            print(self.relations)
        else:
            text = '不存在text'
            self.relations["text"] = text
        time.sleep(1)  # 添加强制等待 1 秒
        return self.relations

    # 自定义存储关联信息
    def save_params(self, key='', value=''):
        try:
            if key.startswith("reg"):
                if value.startswith("//") or value.startswith("com"):
                    ele = self.__find_ele(value)
                    info = ele.info
                    value = info["text"]
                    value = re.findall(r'\d{14}', value)[0]
                else:
                    value = value
            else:
                if value.startswith("//") or value.startswith("com"):
                    ele = self.__find_ele(value)
                    info = ele.info
                    value = info["text"]
                else:
                    value = value
            self.relations[key] = value
            print(self.relations)
        except Exception as e:
            self.attachshoot()
            self.restart()
            pytest.fail()
        time.sleep(1)  # 添加强制等待 1 秒
        return self.relations

    def sleep(self, num):
        """等待"""
        if type(num) == int:
            sleep(num)
        else:
            sleep(float(num))

    def close(self, package=''):
        self.sleep(1)
        self.driver.app_stop(package)

    def back(self, num=""):
        if num:
            for i in range(int(num)):
                self.driver.press("back")
        self.driver.press("back")

    def restart(self):
        # self.attachshoot()
        self.close(self.package)
        self.appstart(self.package)
        self.sleep(1)
        # self.click("主站")

    def calculate_expected_amount(product_name, discount):
        # 根据业务规则计算预期应付金额
        if product_name == "特定商品名称":
            original_price = 100  # 假设特定商品的原价为 100
            return original_price * discount
        else:
            return 0

    @relations
    def assert_equal(self, exp_value='', locator=''):
        ele = self.__find_ele(locator)
        try:
            try:
                info = ele.info
                if info:
                    act_value = info["text"]
                    logging.info("预期文案：" + exp_value)
                    logging.info("检查文案：" + act_value)
                    # self.attachshoot()
                    # allure.attach(open(self.relations["picture_file"], 'rb').read(), 'Screenshot',
                    #               attachment_type=allure.attachment_type.PNG)
                else:
                    act_value = "不存在元素"
                    logging.info("预期文案：" + exp_value)
                    logging.info("检查文案：" + act_value)
            except Exception as e:
                act_value = "不存在元素"
                logging.info("预期文案：" + exp_value)
                logging.info("检查文案：" + act_value)

            if act_value.__contains__(exp_value) or exp_value.__contains__(act_value):
                pass
            else:
                self.attachshoot()
                self.restart()
                pytest.fail()
        except Exception as e:
            self.attachshoot()
            self.restart()
            pytest.fail()
        time.sleep(1)  # 添加强制等待 1 秒

    def attachshoot(self):
        time_stamp = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time.time()))
        picture_file = './ErrorImg/error{0}.png'.format(time_stamp)
        self.relations["picture_file"] = picture_file
        print(self.relations['picture_file'])
        self.driver.screenshot(picture_file)
        allure.attach(open(self.relations["picture_file"], 'rb').read(), 'Fail Screenshot',
                      attachment_type=allure.attachment_type.PNG)
        time.sleep(1)  # 添加强制等待 1 秒
        return picture_file

    # 在坐标处输入文本信息
    def input_at_coordinates(self, x, y, text):
        """
            在指定坐标处输入文本。
            参数：
            - d：uiautomator2 对象。
            - x：横坐标。
            - y：纵坐标。
            - text：要输入的文本。
        """
        try:
            # 确保 x, y 都是浮点数
            x = float(x)
            y = float(y)
            # 模拟点击坐标
            self.driver.click(x, y)
            logging.info("输入：" + text)
            # 清除原来输入框内的的文本
            self.driver.clear_text()
            # 重新输入文本
            self.driver.send_keys(str(text), clear=True)
            print("输入：" + text)
        except Exception as e:
            print(f"输入时出现错误：{e}")

            # 在坐标处输入文本信息

    def input_at_coordinates2(self, location1="", location2="", text=""):
        """
            在指定坐标处输入文本。
        """
        try:
            if text:
                # 模拟点击坐标
                logging.info("点击坐标：(" + location1 + "," + location2 + ")")
                self.driver.click(float(location1), float(location2))
                logging.info("输入：" + text)
                # 清除原来输入框内的的文本
                self.driver.clear_text()
                # 重新输入文本
                self.driver.send_keys(str(text), clear=True)
                print("输入：" + text)
            elif location1.startswith("("):
                # 模拟点击坐标
                self.pxclick(location1)
                logging.info("输入：" + text)
                # 清除原来输入框内的的文本
                self.driver.clear_text()
                # 重新输入文本
                self.driver.send_keys(str(text), clear=True)
                print("输入：" + text)

        except Exception as e:
            print(f"输入时出现错误：{e}")

    # 清空文本框内容
    def clear(self, identifier):
        try:
            if isinstance(identifier, str):
                element = self.driver.xpath(identifier) if identifier.startswith('//') else self.driver(text=identifier)
                if element.exists:
                    element.click()
                    logging.info("清空文本框内容")
                    self.driver.clear_text()
                    print("清空文本框内容")
                else:
                    raise ValueError(
                        f"在当前页面找不到文本为'{identifier}'的元素。请检查元素是否存在或者定位方式是否正确。")
        except Exception as e:
            print(f"清除文本时出现错误：{e}")

    # 清除指定坐标的文本内容
    def clear_xy(self, x, y):
        try:
            # 确保 x, y 都是浮点数
            x = float(x)
            y = float(y)

            # 点击指定坐标
            self.driver.click(x, y)
            logging.info(f"点击坐标 ({x}, {y})")

            # 清除文本
            self.driver.clear_text()
            logging.info("清空文本框内容")
            print("清空文本框内容")
        except ValueError as ve:
            print(f"输入参数类型错误：{ve}")
            logging.error(f"输入参数类型错误：{ve}")
        except Exception as e:
            print(f"清除文本时出现错误：{e}")
            logging.error(f"清除文本时出现错误：{e}")

    # 输入点击二合一
    def assetrelement(self, locator="", keyword="", value=""):
        try:
            if not locator or not keyword:
                raise ValueError("locator 和 keyword 参数不能为空")

            ele = self.__find_ele(locator)
            if ele is not None:
                logger.info(f"识别到此元素：{locator}")
                if keyword == "click":
                    ele.click()
                    logger.info("执行点击操作")
                elif keyword == "input":
                    if value:
                        # 清除现有文本
                        ele.send_keys(Keys.CONTROL + "a")
                        ele.send_keys(Keys.DELETE)
                        # 输入新文本
                        ele.send_keys(value)
                        logger.info(f"输入：{value}")
                    else:
                        logger.warning("未提供输入值")
                else:
                    logger.warning(f"无法识别的操作: {keyword}")
            else:
                logger.warning(f"不存在 [{locator}] 元素")
        except ValueError as ve:
            logger.error(f"输入参数无效: {ve}")
        except Exception as e:
            logger.error(f"操作元素时出现错误：{e}")

    # 坐标点击
    def pxclick(self, key=""):
        try:
            # 确保 key 不为空
            if not key.strip():
                raise ValueError("参数 'key' 不能为空")

            # 延迟1秒
            time.sleep(1)

            # 清除括号并分割字符串
            cleaned_key = key.strip("()")
            parts = cleaned_key.split(",")

            # 检查是否包含两个部分
            if len(parts) != 2:
                raise ValueError(f"参数 '{key}' 格式不正确，应为 '(x,y)' 形式")

            # 将分割后的字符串转换为浮点数
            float_numbers = [float(part.strip()) for part in parts]

            # 提取 x 和 y 坐标
            x, y = float_numbers

            # 记录日志信息
            logger.info(f"点击坐标: ({x}, {y})")
            print(f"点击坐标: ({x}, {y})")

            # 执行点击操作
            self.driver.click(x, y)

        except ValueError as ve:
            logger.error(f"输入参数无效: {ve}")
            print(f"输入参数无效: {ve}")
        except Exception as e:
            logger.error(f"点击操作时出现错误: {e}")
            print(f"点击操作时出现错误: {e}")


if __name__ == '__main__':
    # os.system('rd /s/q ErrorImg')
    # sleep(5)
    # os.mkdir("ErrorImg")
    app = App()
    app.dev_connect()
    style_number = app.style_number()  # 引用style_number自动生成款号，不加这一行的话，也可以直接用app.style_number()
    # app.savclicke_params("账号",'13631258769')
    # app.attachshoot()
    # try:
    #     app.click('//*[@resource-id="com.yishouapp.fumi:id/main_tab5"]/android.widget.ImageView[1]')
    # except Exception as e:
    #     app.driver.screenshot("error.png")
    # app.sleep(2)
    # time_stamp = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time.time()))
    # picture_file = os.path.join(os.getcwd(), '../lib/error/error{0}.png'.format(time_stamp))
    # app.attachshoot(picture_file)
